package com.pobing.zebra.rfid;

import android.app.Activity;
import android.os.AsyncTask;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.zebra.rfid.api3.ACCESS_OPERATION_CODE;
import com.zebra.rfid.api3.ACCESS_OPERATION_STATUS;
import com.zebra.rfid.api3.Antennas;
import com.zebra.rfid.api3.ENUM_TRANSPORT;
import com.zebra.rfid.api3.ENUM_TRIGGER_MODE;
import com.zebra.rfid.api3.HANDHELD_TRIGGER_EVENT_TYPE;
import com.zebra.rfid.api3.INVENTORY_STATE;
import com.zebra.rfid.api3.InvalidUsageException;
import com.zebra.rfid.api3.OperationFailureException;
import com.zebra.rfid.api3.RFIDReader;
import com.zebra.rfid.api3.ReaderDevice;
import com.zebra.rfid.api3.Readers;
import com.zebra.rfid.api3.RfidEventsListener;
import com.zebra.rfid.api3.RfidReadEvents;
import com.zebra.rfid.api3.RfidStatusEvents;
import com.zebra.rfid.api3.SESSION;
import com.zebra.rfid.api3.SL_FLAG;
import com.zebra.rfid.api3.START_TRIGGER_TYPE;
import com.zebra.rfid.api3.STATUS_EVENT_TYPE;
import com.zebra.rfid.api3.STOP_TRIGGER_TYPE;
import com.zebra.rfid.api3.TagData;
import com.zebra.rfid.api3.TriggerInfo;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.PluginResult;
import org.epctagcoder.parse.SGTIN.ParseSGTIN;
import org.epctagcoder.result.SGTIN;
import org.json.JSONArray;
import org.json.JSONException;

import java.util.ArrayList;
import java.util.List;

/**
 * Zebra RFID手持设备插件
 */
public class ZebraRFIDScannerPlugin extends CordovaPlugin implements Readers.RFIDReaderEventHandler {

    // 插件回传
    private CallbackContext receiveScanCallback = null;

    final static String TAG = "ZebraRFIDScannerPlugin";
    private static Readers readers;
    private static ArrayList<ReaderDevice> availableRFIDReaderList;
    private static ReaderDevice readerDevice;
    private static RFIDReader reader;
    private EventHandler eventHandler;
    private int MAX_POWER = 200;
    private Activity context;
    String readername = "RFD8500123";

    /**
     * 插件初始化
     * @param cordova
     * @param webView
     */
    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova,webView);

        context = cordova.getActivity();
        this.initSDK();
    }

    /**
     * 插件访问接口
     * @param action          The action to execute.
     * @param args            The exec() arguments.
     * @param callbackContext The callback context used when calling back into JavaScript.
     * @return
     * @throws JSONException
     */
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        Log.v(TAG, "============== execute ===========: " + action);

        if (action.equals("requestScan")) {
            Log.v(TAG, "isReaderConnected：" + isReaderConnected());
            Log.v(TAG, "requestScan");
            // 连接一下
            connect();
            receiveScanCallback = callbackContext;
            return true;
        }

        if (action.equals("setScanParams")) {
            Short transmitPowerIndex = Short.parseShort((String) args.get(0));
            if (transmitPowerIndex.intValue() > 0 && transmitPowerIndex.intValue() <= 300) {
                MAX_POWER = transmitPowerIndex;
            }
            Log.v(TAG, "setScanParams  param: " + transmitPowerIndex);
            // 执行配置初始化
            try {
                Log.v(TAG, "setScanParams 执行配置初始化......");
                Antennas.AntennaRfConfig config = null;
                config = reader.Config.Antennas.getAntennaRfConfig(1);
                config.setTransmitPowerIndex(MAX_POWER);
                config.setrfModeTableIndex(0);
                config.setTari(0);
                reader.Config.Antennas.setAntennaRfConfig(1, config);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 连接一下
            connect();
            receiveScanCallback = callbackContext;
            return true;
        }

        Log.v(TAG, "============== done   ===========: " + action);
        return false;
    }


    public void initSDK() {
        Log.d(TAG, "InitSDK");

        // 判断应该创建任务还是执行任务
        if (readers == null) {
            new CreateInstanceTask().execute();
        } else
            new ConnectionTask().execute();
    }

    // handler for receiving reader appearance events
    @Override
    public void RFIDReaderAppeared(ReaderDevice readerDevice) {
        Log.d(TAG, "RFIDReaderAppeared " + readerDevice.getName());
        new ConnectionTask().execute();
    }

    @Override
    public void RFIDReaderDisappeared(ReaderDevice readerDevice) {
        Log.d(TAG, "RFIDReaderDisappeared " + readerDevice.getName());
        if (readerDevice.getName().equals(reader.getHostName()))
            disconnect();
    }

    // Enumerates SDK based on host device
    private class CreateInstanceTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... voids) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->CreateInstanceTask doInBackground 开始");
            Log.d(TAG, "CreateInstanceTask");
            // Based on support available on host device choose the reader type
            InvalidUsageException invalidUsageException = null;
            readers = new Readers(context, ENUM_TRANSPORT.ALL);
            availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
            Log.d("==========", "availableRFIDReaderList:  " + availableRFIDReaderList.size());
            Log.v(TAG, "readers：" + readers);
            Log.v(TAG, "invalidUsageException：" + invalidUsageException);
            if (invalidUsageException != null) {
                readers.Dispose();
                readers = null;
                if (readers == null) {
                    readers = new Readers();
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->CreateInstanceTask onPostExecute 开始");
            super.onPostExecute(aVoid);
            Log.v(TAG, "readers：" + readers);
            new ConnectionTask().execute();
        }
    }

    private class ConnectionTask extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... voids) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->ConnectionTask doInBackground 开始");
            Log.d(TAG, "ConnectionTask");
            GetAvailableReader();
            Log.v(TAG, "isReaderConnected：" + isReaderConnected());
            if (reader != null)
                return connect();
            return "Failed to find or connect reader";
        }

        @Override
        protected void onPostExecute(String result) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->ConnectionTask onPostExecute 开始");
            super.onPostExecute(result);

            Log.v(TAG, "isReaderConnected：" + isReaderConnected());

            // 执行配置初始化
            try {
                Log.v(TAG, "执行配置初始化......");
                Antennas.AntennaRfConfig config = null;
                config = reader.Config.Antennas.getAntennaRfConfig(1);
                config.setTransmitPowerIndex(300);
                config.setrfModeTableIndex(0);
                config.setTari(0);
                reader.Config.Antennas.setAntennaRfConfig(1, config);
            } catch (InvalidUsageException e) {
                e.printStackTrace();
            } catch (OperationFailureException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void GetAvailableReader() {
        Log.d(TAG, "ZebraRFIDScannerPlugin->GetAvailableReader 开始");
        Log.d(TAG, "GetAvailableReader");
        Log.d(TAG, "readers:" + readers);
        if (readers != null) {
            readers.attach(this);
            if (readers.GetAvailableRFIDReaderList() != null) {
                availableRFIDReaderList = readers.GetAvailableRFIDReaderList();
                Log.d(TAG, "availableRFIDReaderList:" + availableRFIDReaderList.size());
                if (availableRFIDReaderList.size() != 0) {
                    // if single reader is available then connect it
                    if (availableRFIDReaderList.size() == 1) {
                        readerDevice = availableRFIDReaderList.get(0);
                        reader = readerDevice.getRFIDReader();
                    } else {
                        // search reader specified by name
                        for (ReaderDevice device : availableRFIDReaderList) {
                            if (device.getName().equals(readername)) {
                                readerDevice = device;
                                reader = readerDevice.getRFIDReader();
                            }
                        }
                    }
                }
            }
        }
    }

    private boolean isReaderConnected() {
        if (reader != null && reader.isConnected())
            return true;
        else {
            Log.d(TAG, "reader is not connected");
            return false;
        }
    }

    private synchronized String connect() {
        Log.d(TAG, "ZebraRFIDScannerPlugin->connect 开始");
        if (reader != null) {
            Log.d(TAG, "connect " + reader.getHostName());
            try {
                if (!reader.isConnected()) {
                    // Establish connection to the RFID Reader
                    reader.connect();
                    ConfigureReader();
                    return "Connected";
                }
            } catch (InvalidUsageException e) {
                e.printStackTrace();
            } catch (OperationFailureException e) {
                e.printStackTrace();
                Log.d(TAG, "OperationFailureException " + e.getVendorMessage());
                String des = e.getResults().toString();
                return "Connection failed" + e.getVendorMessage() + " " + des;
            }
        }
        return "";
    }

    private void ConfigureReader() {
        Log.d(TAG, "ZebraRFIDScannerPlugin->ConfigureReader 开始");
        Log.d(TAG, "ConfigureReader " + reader.getHostName());
        if (reader.isConnected()) {
            TriggerInfo triggerInfo = new TriggerInfo();
            triggerInfo.StartTrigger.setTriggerType(START_TRIGGER_TYPE.START_TRIGGER_TYPE_IMMEDIATE);
            triggerInfo.StopTrigger.setTriggerType(STOP_TRIGGER_TYPE.STOP_TRIGGER_TYPE_IMMEDIATE);
            try {
                // receive events from reader
                if (eventHandler == null)
                    eventHandler = new EventHandler();
                reader.Events.addEventsListener(eventHandler);
                // HH event
                reader.Events.setHandheldEvent(true);
                // tag event with tag data
                reader.Events.setTagReadEvent(true);
                reader.Events.setAttachTagDataWithReadEvent(false);
                // set trigger mode as rfid so scanner beam will not come
                reader.Config.setTriggerMode(ENUM_TRIGGER_MODE.RFID_MODE, true);
                // set start and stop triggers
                reader.Config.setStartTrigger(triggerInfo.StartTrigger);
                reader.Config.setStopTrigger(triggerInfo.StopTrigger);

                // power levels are index based so maximum power supported get the last one
                // MAX_POWER = reader.ReaderCapabilities.getTransmitPowerLevelValues().length - 1;
                // set antenna configurations
                Antennas.AntennaRfConfig config = reader.Config.Antennas.getAntennaRfConfig(1);

                // 获取默认功率
                config.setTransmitPowerIndex(MAX_POWER);
                config.setrfModeTableIndex(0);
                config.setTari(0);
                reader.Config.Antennas.setAntennaRfConfig(1, config);
                // Set the singulation control
                Antennas.SingulationControl s1_singulationControl = reader.Config.Antennas.getSingulationControl(1);
                s1_singulationControl.setSession(SESSION.SESSION_S0);
                s1_singulationControl.Action.setInventoryState(INVENTORY_STATE.INVENTORY_STATE_A);
                s1_singulationControl.Action.setSLFlag(SL_FLAG.SL_ALL);
                reader.Config.Antennas.setSingulationControl(1, s1_singulationControl);
                // delete any prefilters
                reader.Actions.PreFilters.deleteAll();
                //
            } catch (InvalidUsageException | OperationFailureException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void disconnect() {
        Log.d(TAG, "ZebraRFIDScannerPlugin->disconnect 开始");
        Log.d(TAG, "disconnect " + reader);
        try {
            if (reader != null) {
                reader.Events.removeEventsListener(eventHandler);
                reader.disconnect();
                // 关闭连接
            }
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Read/Status Notify handler
    // Implement the RfidEventsLister class to receive event notifications
    public class EventHandler implements RfidEventsListener {
        // Read Event Notification
        public void eventReadNotify(RfidReadEvents e) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->EventHandler eventReadNotify 开始");
            // Recommended to use new method getReadTagsEx for better performance in case of large tag population
            TagData[] myTags = reader.Actions.getReadTags(100);
            if (myTags != null) {
                for (int index = 0; index < myTags.length; index++) {
                    Log.d(TAG, "Tag ID " + myTags[index].getTagID());
                    if (myTags[index].getOpCode() == ACCESS_OPERATION_CODE.ACCESS_OPERATION_READ &&
                            myTags[index].getOpStatus() == ACCESS_OPERATION_STATUS.ACCESS_SUCCESS) {
                        if (myTags[index].getMemoryBankData().length() > 0) {
                            Log.d(TAG, " Mem Bank Data " + myTags[index].getMemoryBankData());
                        }
                    }
                    if (myTags[index].isContainsLocationInfo()) {
                        short dist = myTags[index].LocationInfo.getRelativeDistance();
                        Log.d(TAG, "Tag relative distance " + dist);
                    }
                }
                // possibly if operation was invoked from async task and still busy
                // handle tag data responses on parallel thread thus THREAD_POOL_EXECUTOR
                new AsyncDataUpdate().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, myTags);
            }
        }

        // Status Event Notification
        public void eventStatusNotify(RfidStatusEvents rfidStatusEvents) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->EventHandler eventStatusNotify 开始");

            Log.d(TAG, "Status Notification: " + rfidStatusEvents.StatusEventData.getStatusEventType());
            Log.d(TAG, "Status Notification HandheldTriggerEventData: " + rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent());
            if (rfidStatusEvents.StatusEventData.getStatusEventType() == STATUS_EVENT_TYPE.HANDHELD_TRIGGER_EVENT) {
                if (rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent() == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_PRESSED) {
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            // context.handleTriggerPress(true);
                            performInventory();
                            return null;
                        }
                    }.execute();
                }
                if (rfidStatusEvents.StatusEventData.HandheldTriggerEventData.getHandheldEvent() == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_RELEASED) {
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            // context.handleTriggerPress(false);
                            stopInventory();
                            return null;
                        }
                    }.execute();
                }
            }
        }
    }

    synchronized void performInventory() {
        // check reader connection
        if (!isReaderConnected())
            return;
        try {
            reader.Actions.Inventory.perform();
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        }
    }

    synchronized void stopInventory() {
        // check reader connection
        if (!isReaderConnected())
            return;
        try {
            reader.Actions.Inventory.stop();
        } catch (InvalidUsageException e) {
            e.printStackTrace();
        } catch (OperationFailureException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取扫描结果，并格式化回传
     */
    private class AsyncDataUpdate extends AsyncTask<TagData[], Void, Void> {
        @Override
        protected Void doInBackground(TagData[]... params) {
            Log.d(TAG, "ZebraRFIDScannerPlugin->AsyncDataUpdate doInBackground 开始");
            // 读取扫描结果
            TagData[] tagData = params[0];
            ArrayList<PluginResult> arguments = new ArrayList<PluginResult>();
            List<String> list = new ArrayList<>();
            for (int index = 0; index < tagData.length; index++) {
                // 去重
                if (list.indexOf(tagData[index].getTagID()) == -1) {
                    list.add(tagData[index].getTagID());
                }
            }

            // 将TagId列表解码为Json字符串
            String result = decodeTagId(list);

            arguments.add(new PluginResult(PluginResult.Status.OK, result));
            PluginResult progressResult = new PluginResult(PluginResult.Status.OK, arguments);
            progressResult.setKeepCallback(true);

            if (receiveScanCallback == null) {
                Log.v(TAG, "receiveScanCallback is null.");
            } else {
                Log.v(TAG, "receiveScanCallback is not null.");
                receiveScanCallback.sendPluginResult(progressResult);
            }
            return null;
        }
    }

    /**
     * 解码TagId，默认为 SGTIN类型的 EPC
     * barcode结果 = companyPrefix（数值，首位消 0） + itemReference + checkDigit
     * @param tagIdList
     * @return
     */
    public String decodeTagId(List<String> tagIdList) {
        String result = null;
        ParseSGTIN parseSGTIN = null;
        SGTIN sgtin = null;
        List<SGTIN> list = new ArrayList<>();
        try {
            // 遍历并解析
            for (String tagId : tagIdList) {
                parseSGTIN = ParseSGTIN.Builder().withRFIDTag(tagId).build();
                sgtin = parseSGTIN.getSGTIN();
                Log.v(TAG, "sgtin:" + sgtin.toString() );
                list.add(sgtin);
            }
            Log.v(TAG, "list:" + list.toString() );
            result = JSON.toJSON(list).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

}
